import tkinter as tk
from tkinter import filedialog, messagebox
from scapy.all import *
from scapy.utils import wrpcap


class PacketAnalyzer:
    def __init__(self, root):
        self.root = root
        self.root.title("Advanced Packet Analyzer")
        self.packets = []

        # 主显示区域
        self.text = tk.Text(root, wrap=tk.WORD)
        self.scroll = tk.Scrollbar(root, command=self.text.yview)
        self.text.configure(yscrollcommand=self.scroll.set)
        self.text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self.scroll.pack(side=tk.RIGHT, fill=tk.Y)

        # 控制区域
        control_frame = tk.Frame(root)
        control_frame.pack(fill=tk.X)

        # 过滤条件
        filter_frame = tk.Frame(control_frame)
        filter_frame.pack(fill=tk.X, pady=5)
        tk.Label(filter_frame, text="BPF Filter:").pack(side=tk.LEFT)
        self.filter_entry = tk.Entry(filter_frame, width=40)
        self.filter_entry.pack(side=tk.LEFT, padx=5)
        self.filter_entry.insert(0, "tcp or udp or icmp")

        # 按钮区域
        btn_frame = tk.Frame(control_frame)
        btn_frame.pack(fill=tk.X, pady=5)
        self.start_btn = tk.Button(btn_frame, text="Start Sniffing",
                                   command=self.start_sniffing)
        self.start_btn.pack(side=tk.LEFT, padx=5)
        self.stop_btn = tk.Button(btn_frame, text="Stop",
                                  command=self.stop_sniffing, state=tk.DISABLED)
        self.stop_btn.pack(side=tk.LEFT, padx=5)
        self.save_btn = tk.Button(btn_frame, text="Save to File",
                                  command=self.save_packets)
        self.save_btn.pack(side=tk.LEFT, padx=5)
        self.clear_btn = tk.Button(btn_frame, text="Clear",
                                   command=self.clear_output)
        self.clear_btn.pack(side=tk.LEFT, padx=5)

        self.sniffer = None

    def packet_callback(self, packet):
        self.packets.append(packet)
        info = ""
        if packet.haslayer(IP):
            ip_src = packet[IP].src
            ip_dst = packet[IP].dst
            info += f"[{len(self.packets)}] IP {ip_src} -> {ip_dst} | "

            if packet.haslayer(TCP):
                tcp_layer = packet[TCP]
                info += f"TCP {tcp_layer.sport}->{tcp_layer.dport} "
                flags = []
                if tcp_layer.flags & 0x02: flags.append("SYN")
                if tcp_layer.flags & 0x10: flags.append("ACK")
                if flags: info += f"Flags: {'|'.join(flags)} "

            elif packet.haslayer(UDP):
                udp_layer = packet[UDP]
                info += f"UDP {udp_layer.sport}->{udp_layer.dport} "

            elif packet.haslayer(ICMP):
                icmp_layer = packet[ICMP]
                info += f"ICMP type:{icmp_layer.type} code:{icmp_layer.code} "

            if packet.haslayer(Raw):
                payload = packet[Raw].load
                info += f"\nPayload: {payload[:100]}{'...' if len(payload) > 100 else ''}"

        self.text.insert(tk.END, info + "\n" + "-" * 80 + "\n")
        self.text.see(tk.END)

    def start_sniffing(self):
        self.packets = []
        self.text.delete(1.0, tk.END)
        filter_expr = self.filter_entry.get()
        self.sniffer = threading.Thread(
            target=lambda: sniff(prn=self.packet_callback,
                                 filter=filter_expr,
                                 store=False))
        self.sniffer.daemon = True
        self.sniffer.start()
        self.start_btn.config(state=tk.DISABLED)
        self.stop_btn.config(state=tk.NORMAL)

    def stop_sniffing(self):
        if self.sniffer:
            os.kill(os.getpid(), signal.SIGINT)  # 优雅终止sniff
        self.start_btn.config(state=tk.NORMAL)
        self.stop_btn.config(state=tk.DISABLED)

    def save_packets(self):
        if not self.packets:
            messagebox.showwarning("Warning", "No packets to save!")
            return
        filename = filedialog.asksaveasfilename(
            defaultextension=".pcap",
            filetypes=[("PCAP files", "*.pcap"), ("All files", "*.*")])
        if filename:
            wrpcap(filename, self.packets)
            messagebox.showinfo("Info", f"Saved {len(self.packets)} packets to {filename}")

    def clear_output(self):
        self.text.delete(1.0, tk.END)


if __name__ == "__main__":
    import threading
    import os
    import signal

    root = tk.Tk()
    analyzer = PacketAnalyzer(root)
    root.mainloop()